%%% @doc
%%%модуль, который будет работать с mnesia: тесты в tests_table, результаты в states_table
%%% @end
-module(test_storage).
-include("definitions.hrl").

%% API
-export([init/1, getTest/1, addTest/1, start/1, stop/0, getAllTestIds/0, getState/1,
  getLastStateForDevice/1, updateState/1, getNextId/0, insertSamples/0]).
%выполняется на ноде DB_NODE
start(ServerPid) ->
  register(testStorage, spawn(test_storage, init, [ServerPid]))
.

init(ServerPid) ->
  %io:format(atom_to_list(net_adm:ping(?MAIN_NODE))),
  %spawn(?MAIN_NODE, id_manager, start, []),
  spawn(id_manager, start, []),
  initMnesia(),
  ServerPid ! {ok, started},
  %io:format("sent~n"),
  eventLoop()
.

initMnesia() ->
  case ?DEBUG of
    true ->
      ok = mnesia:delete_schema([node()]),
      ok = mnesia:create_schema([node()]),
      ok = mnesia:start(),
      {atomic, ok} = mnesia:create_table(testsTable, [{disc_copies, [node()]}, {attributes, record_info(fields, testCase)}, {record_name, testCase}]),
      {atomic, ok} = mnesia:create_table(statesTable, [{disc_copies, [node()]}, {attributes, record_info(fields, stateRecord)}, {record_name, stateRecord}])
  ;
    false ->
      ok = mnesia:start()
  end,
  ok = mnesia:wait_for_tables([testsTable, statesTable], 3000)

.

-spec insertSamples() -> any().
insertSamples() ->
  Bits = [X || X <- lists:seq(1, 100)],
  Samples =
    [
      #testCase{description = "d1", bits = Bits},
      #testCase{description = "d2", bits = Bits},
      #testCase{description = "d3", bits = Bits},
      #testCase{description = "d4", bits = Bits},
      #testCase{description = "d5", bits = Bits},
      #testCase{description = "d6", bits = Bits},
      #testCase{description = "d7", bits = Bits},
%%       #testCase{description = "d8", bits = Bits},
%%       #testCase{description = "d9", bits = Bits},
%%       #testCase{description = "d10", bits = Bits},
%%       #testCase{description = "d11", bits = Bits},
%%       #testCase{description = "d12", bits = Bits},
%%       #testCase{description = "d13", bits = Bits},
%%       #testCase{description = "d14", bits = Bits},
%%       #testCase{description = "d15", bits = Bits},
      #testCase{description = "d16", bits = Bits}
    ],
  InsLambda = fun(Test) ->
    Id =
      addTest(Test)
  %, io:format("add ~w~n", [Id])
  end,
  lists:foreach(InsLambda, Samples)
.

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%интерфейс
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

getNextId() ->
  id_manager:get_next_id().

stop() ->
  try
    %{?DB_NODE, testStorage} ! stop
    testStorage ! stop
  catch
    error :_ ->
      ok
  end,
  io:format("testStorage finished~n").
%mnesia:delete_schema(tests_table),

-spec addTest(testCase) -> integer().
addTest(Test) ->
  %{?DB_NODE, testStorage} ! {put, Test, self()},
  testStorage ! {put, Test, self()},
  %io:format("sent put~n"),
  Rez =
    receive {nextId, Id} when is_integer(Id) ->
      %io:format("received answer to add~n"),
      Id end,
  Rez.

-spec getTest(integer()) -> testCase.
getTest(TestId) ->
  %{?DB_NODE, testStorage} ! {get, TestId, self()},
  testStorage ! {get, TestId, self()},
  receive
    {yourTest, Test} -> Test
  end.

-spec getAllTestIds() -> [integer()].
getAllTestIds() ->
  % {?DB_NODE, testStorage} ! {all, self()},
  testStorage ! {all, self()},
  receive
    {testIds, KeyList} -> KeyList
  end.

getState(StateId) ->
  %{?DB_NODE, testStorage} ! {getState, StateId, self()},
  testStorage ! {getState, StateId, self()},
  receive
    State -> State
  end.

getLastStateForDevice(DeviceId) ->
  %{?DB_NODE, testStorage} ! {getLastForDevice, DeviceId, self()},
  testStorage ! {getLastForDevice, DeviceId, self()},
  receive
    {restored, #stateRecord{id = Id, answer = Ans, bitsRun = Bits, completed = Completed, deviceId = IdDev, testId = TestId, fails = Fails}} ->
      #stateRecord{id = Id, answer = Ans, bitsRun = Bits, completed = Completed, deviceId = IdDev, testId = TestId, fails = Fails}
  end.

updateState(State) ->
  %{?DB_NODE, testStorage} ! {updateState, State, self()}.
  testStorage ! {updateState, State, self()}.

eventLoop() ->
  receive
  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  %% testCases messages
  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    {get, TestId, From} ->
      getTest(TestId, From),
      eventLoop();

    {put, Test, From} ->
      %io:format("sams add~n"),
      addTest(Test, From),
      %io:format("sams added~n"),
      eventLoop();

    {all, From} ->
      getAllTestsIds(From),
      eventLoop();
  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  %% stateRecords messages
  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    {getState, StateId, From} ->
      getState(StateId, From),
      eventLoop();

    {updateState, State, From} ->
      updateState(State, From),
      eventLoop();

    {getLastForDevice, DeviceId, From} ->
      getLastStateForDevice(DeviceId, From),
      eventLoop();

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% stop
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    stop ->
      mnesia:dump_tables([testsTable, statesTable]),
      mnesia:stop(),
      id_manager:stop(),
      ok
  end.

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%реализация
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
getTest(TestId, From) ->
  Tran = fun() ->
    Test = mnesia:read({testsTable, TestId}),   %match_object(#testCase{id = TestId}),
    Res = case Test of
            [] ->
              error(no_test_with_id, TestId);
    %#testCase{description = "no such test"};
            [H | _] -> H;
            {aborted, Reason} -> error(Reason)
          end,
    Res
  end,
  {atomic, Result} = mnesia:sync_transaction(Tran),
  From ! {yourTest, Result}.

addTest(Test, From) ->
  NextId = getNextId(),
  TestToWrite = Test#testCase{id = NextId},
  Tran = fun() ->
    ok = mnesia:write(testsTable, TestToWrite, write)
  end,
  {atomic, ok} = mnesia:sync_transaction(Tran),
  From ! {nextId, NextId}
.

getAllTestsIds(From) ->
  Tran = fun() -> mnesia:all_keys(testsTable) end,
  {atomic, KeyList} = mnesia:sync_transaction(Tran),
  From ! {testIds, KeyList} .

getState(StateId, From) ->
  Tran = fun() ->
    States = mnesia:read({statesTable, StateId}),
    State = case States of
              [] ->
                io:format("test_storage:getState: state is not found~n"),
                #stateRecord{id = StateId};
              [H | _] -> H;
              {aborted, Reason} -> error(Reason)
            end,
    State
  end,
  {atomic, Result} = mnesia:sync_transaction(Tran),
  From ! Result.

updateState(State, From) ->
  Tran = fun() ->
    mnesia:write(statesTable, State, write),
    ok
  end,
  {atomic, ok} = mnesia:sync_transaction(Tran),
  From ! ok.

getLastStateForDevice(DeviceId, From) ->
  Tran = fun() ->
    List = mnesia:match_object(statesTable, {stateRecord, '_', '_', DeviceId, '_', '_', '_', '_'}, read),
    case List of

      [] ->
        io:format("getLastStateForDevice: no states for this device~n"),
        #stateRecord{};
      {aborted, Reason} -> error(Reason);

      AllStatesForDevice ->
        SelectorId = fun(Element) ->
          case Element#stateRecord.completed of
            true ->
              Element#stateRecord.id;% * -1 не быть первым
            false -> Element#stateRecord.id
          %Element#stateRecord.id
          end
        end,
        MaxIds = lists:map(SelectorId, AllStatesForDevice),
        MaxId = lists:max(MaxIds),
        FilterById = fun(El) ->
          El#stateRecord.id == abs(MaxId)
        end,
        Result = hd(lists:filter(FilterById, AllStatesForDevice)),
        Result
    end
  end,
  {atomic, Result} = mnesia:sync_transaction(Tran),
  From ! {restored, Result}.%последнее состояние или заглушка.